
#include <BScene>
#include "IGlobal.h"
#include "member_BScene.h"

using namespace BWE;

#define member					(*(member_BScene*)_ptr)
#define member_allocate()		_ptr = new member_BScene(this)
#define member_release()		delete (member_BScene*)_ptr

BScene::BScene()
{
	member_allocate();
	app_scenes.append(this);
}
BScene::BScene(const BString& name)
{
	member_allocate();
	app_scenes.append(this);
	this->setName(name);
}
BScene::~BScene()
{
	clear();
	app_scenes.remove(this);
	member_release();
}

void BScene::setOptimized(bool optimized)
{
	if (optimized && member.sceneOctTree.empty())
	{
		member.sceneOctTree = new SceneOctTree();
		member.freshOctTree();
		this->emit(Signal_Optimized, true);
		return;
	}
	if (!optimized && member.sceneOctTree.valid())
	{
		member.sceneOctTree = 0;
		this->emit(Signal_Optimized, false);
		return;
	}
}
bool BScene::optimized() const
{
	return !member.sceneOctTree.empty();
}

bool BScene::empty() const
{
	return member.groups.empty() && member.nodes.empty() && member.shapes.empty();
}
const BSpace& BScene::space() const
{
	if (member.dirtySpace)
	{
		member.space.reset();
		if (member.shapes.size())
		{
			member.space = member.shapes.first()->space();
			for (int i = 0; i < member.shapes.size(); i++)
			{
				BShape* shape = member.shapes[i];
				member.space.expand(shape->space());
			}
		}
		if (member.nodes.size())
		{
			if (member.space.empty())
				member.space.expand(member.nodes.first()->space());
			for (int i = 0; i < member.nodes.size(); i++)
			{
				BNode* node = member.nodes[i];
				member.space.expand(node->space());
			}
		}
		if (member.groups.size())
		{
			if (member.space.empty())
				member.space.expand(member.groups.first()->space());
			for (int i = 0; i < member.groups.size(); i++)
			{
				BGroup* group = member.groups[i];
				member.space.expand(group->space());
			}
		}
		member.dirtySpace = false;
		if (member.sceneOctTree)
		{
			member.freshOctTree();
		}
		emit(Signal_SpaceChanged, member.space);
	}
	return member.space;
}

bool BScene::insert(BShape* shape)
{
	if (!shape)
		return false;
	if (member.shapes.contain(shape))
		return false;
	member.shapes.insert(shape);
	member.connect(shape, Signal_Enabled, &member_BScene::slotShapeEnabled);
	member.connect(shape, Signal_Fresh, &member_BScene::slotShapeFresh);
	member.connect(shape, Signal_Dirty, &member_BScene::slotShapeDirty);
	member.connect(shape, Signal_Transformed, &member_BScene::slotShapeTransformed);
	member.dirtySpace = true;
	member.refresh = true;
	if (member.sceneOctTree)
	{
		SceneOctTreeItem item = member.sceneOctTree->create(shape->space());
		item->shapes.append(shape);
		member.shapeSpaceMap[shape] = item;
	}
	this->emit(Signal_Insert, shape);
	return true;
}
bool BScene::remove(BShape* shape)
{
	if (shape && member.shapes.contain(shape))
	{
		member.disconnect((BShape*)shape);
		member.shapes.remove(shape);
		member.dirtySpace = true;
		member.refresh = true;
		if (member.sceneOctTree)
		{
			SceneOctTreeItem& item = member.shapeSpaceMap[shape];
			item->shapes.remove(shape);
			member.shapeSpaceMap.remove(shape);
		}
		this->emit(Signal_Remove, shape);
	}
	return true;
}
bool BScene::contain(const BShape* shape) const
{
	return member.shapes.contain(shape);
}

int BScene::shapeCount() const
{
	return member.shapes.size();
}
BShape* BScene::shape(int index) const
{
	return member.shapes(index);
}
BShape* BScene::shape(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
BShape* BScene::shapeOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->asset() == asset)
			return shape;
	}
	return 0;
}
bool BScene::shapesInSpace(const BSpace& space, BShapeArray& shapes) const
{
	shapes.reset();
	if (member.sceneOctTree)
	{
		SceneOctTreeItem item = member.sceneOctTree->locate(space);
		member.collectShapes(item, shapes);
	}
	else
	{
		for (int i = 0; i < member.shapes.size(); i++)
		{
			BShape* shape = member.shapes[i];
			if (shape->space().intersect(space))
			{
				shapes.append(shape);
			}
		}
	}
	return shapes.size();
}

bool BScene::insert(BNode* node)
{
	if (!node)
		return false;
	if (member.nodes.contain(node))
		return false;
	if (BLight* light = dynamic_cast<BLight*>(node))
	{
		member.lights.insert(light);
	}
	else if (BCamera* camera = dynamic_cast<BCamera*>(node))
	{
		member.cameras.insert(camera);
	}
	member.nodes.insert(node);
	member.connect(node, Signal_Enabled, &member_BScene::slotNodeEnabled);
	member.connect(node, Signal_Fresh, &member_BScene::slotNodeFresh);
	member.connect(node, Signal_Dirty, &member_BScene::slotNodeDirty);
	member.connect(node, Signal_Insert, &member_BScene::slotNodeInsert);
	member.connect(node, Signal_Remove, &member_BScene::slotNodeRemove);
	member.connect(node, Signal_Transformed, &member_BScene::slotNodeTransformed);
	member.dirtySpace = true;
	member.refresh = true;
	if (member.sceneOctTree && member.sceneOctTree->space().contain(node->space()))
	{
		auto item = member.sceneOctTree->create(node->space());
		item->nodes.append(node);
		member.nodeSpaceMap[node] = item;
	}
	this->emit(Signal_Insert, node);
	return true;
}
bool BScene::remove(BNode* node)
{
	if (node && member.nodes.contain(node))
	{
		if (const BLight* light = dynamic_cast<const BLight*>(node))
		{
			member.lights.remove(light);
		}
		else if (const BCamera* camera = dynamic_cast<const BCamera*>(node))
		{
			member.cameras.remove(camera);
		}
		member.disconnect((BNode*)node);
		member.nodes.remove(node);
		member.dirtySpace = true;
		member.refresh = true;
		if (member.sceneOctTree)
		{
			SceneOctTreeItem& item = member.nodeSpaceMap[node];
			item->nodes.remove(node);
			member.nodeSpaceMap.remove(node);
		}
	}
	this->emit(Signal_Remove, node);
	return false;
}
bool BScene::contain(const BNode* node) const
{
	return member.nodes.contain(node);
}

int BScene::nodeCount() const
{
	return member.nodes.size();
}
BNode* BScene::node(int index) const
{
	BNodeHolder node = member.nodes(index);
	return node.ptr();
}
BNode* BScene::node(const BString& name) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->name() == name)
			return node;
	}
	return 0;
}
BNode* BScene::nodeOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		BNode* node = member.nodes[i];
		if (node->asset() == asset)
			return node;
	}
	return 0;
}
bool BScene::nodesInSpace(const BSpace& space, BNodeArray& nodes) const
{
	nodes.reset();
	if (member.sceneOctTree)
	{
		SceneOctTreeItem item = member.sceneOctTree->locate(space);
		member.collectNodes(item, nodes);
	}
	else
	{
		for (int i = 0; i < member.nodes.size(); i++)
		{
			BNode* node = member.nodes[i];
			if (node->space().intersect(space))
			{
				nodes.append(node);
			}
		}
	}
	return nodes.size();
}

bool BScene::insert(BGroup* group)
{
	if (!group)
		return false;
	if (!member.groups.contain(group))
	{
		member.groups.insert(group);
		member.connect(group, Signal_Enabled, &member_BScene::slotGroupEnabled);
		member.connect(group, Signal_Fresh, &member_BScene::slotGroupFresh);
		member.connect(group, Signal_Dirty, &member_BScene::slotGroupDirty);
		member.connect(group, Signal_Insert, &member_BScene::slotGroupInsert);
		member.connect(group, Signal_Remove, &member_BScene::slotGroupRemove);
		member.dirtySpace = true;
		member.refresh = true;
		this->emit(Signal_Insert, group);
		return true;
	}
	return false;
}
bool BScene::remove(BGroup* group)
{
	if (group && member.groups.contain(group))
	{
		BHolder<BGroup> holder(group);
		member.disconnect(group);
		member.groups.remove(group);
		member.dirtySpace = true;
		member.refresh = true;
		this->emit(Signal_Remove, group);
		return true;
	}
	return false;
}
bool BScene::contain(const BGroup* group) const
{
	return member.groups.contain(group);
}

int BScene::groupCount() const
{
	return member.groups.size();
}
BGroup* BScene::group(int index) const
{
	return member.groups(index);
}
BGroup* BScene::group(const BString& name) const
{
	for (int i = 0; i < member.groups.size(); i++)
	{
		BGroup* group = member.groups[i];
		if (group->name() == name)
			return group;
	}
	return 0;
}
BGroup* BScene::groupOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.groups.size(); i++)
	{
		BGroup* group = member.groups[i];
		if (group->asset() == asset)
			return group;
	}
	return 0;
}

bool BScene::insert(BConstraint* constraint)
{
	if (!constraint)
		return false;
	if (member.constraints.contain(constraint))
		return false;
	member.constraints.insert(constraint);
	member.dirtySpace = true;
	member.refresh = true;
	this->emit(Signal_Insert, constraint);
	return true;
}
bool BScene::remove(BConstraint* constraint)
{
	if (constraint && member.constraints.contain(constraint))
	{
		member.disconnect((BConstraint*)constraint);
		member.constraints.remove(constraint);
		member.dirtySpace = true;
		member.refresh = true;
		this->emit(Signal_Remove, constraint);
	}
	return false;
}
bool BScene::contain(const BConstraint* constraint)
{
	return member.constraints.contain(constraint);
}

int BScene::constraintCount() const
{
	return member.constraints.size();
}
BConstraint* BScene::constraint(int index) const
{
	return member.constraints(index);
}
BConstraint* BScene::constraint(const BString& name) const
{
	for (int i = 0; i < member.constraints.size(); i++)
	{
		BConstraint* constraint = member.constraints[i];
		if (constraint->name() == name)
			return constraint;
	}
	return 0;
}
BConstraint* BScene::constraintOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.constraints.size(); i++)
	{
		BConstraint* constraint = member.constraints[i];
		if (constraint->asset() == asset)
			return constraint;
	}
	return 0;
}

void BScene::clearGroups()
{
	while (BGroup* group = member.groups.last())
	{
		remove(group);
	}
	emit(Signal_Dirty);
}
void BScene::clearShapes()
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		member.disconnect(member.shapes[i]);
	}
	if (member.sceneOctTree)
	{
		for (int i = 0; i < member.shapes.size(); i++)
		{
			BShape* shape = member.shapes[i];
			SceneOctTreeItem& item = member.shapeSpaceMap[shape];
			item->shapes.remove(shape);
			member.shapeSpaceMap.remove(shape);
		}
	}
	member.shapes.clear();
	member.dirtySpace = true;
	member.refresh = true;
	emit(Signal_Dirty);
}
void BScene::clearNodes()
{
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.disconnect(member.nodes[i]);
	}
	if (member.sceneOctTree)
	{
		for (int i = 0; i < member.nodes.size(); i++)
		{
			BNode* node = member.nodes[i];
			SceneOctTreeItem& item = member.nodeSpaceMap[node];
			item->nodes.remove(node);
			member.nodeSpaceMap.remove(node);
		}
	}
	member.lights.clear();
	member.cameras.clear();
	member.nodes.clear();
	member.dirtySpace = true;
	member.refresh = true;
	emit(Signal_Dirty);
}
void BScene::clear()
{
	for (int i = 0; i < member.groups.size(); i++)
	{
		member.disconnect(member.groups[i]);
	}
	for (int i = 0; i < member.nodes.size(); i++)
	{
		member.disconnect(member.nodes[i]);
	}
	for (int i = 0; i < member.shapes.size(); i++)
	{
		member.disconnect(member.shapes[i]);
	}
	for (int i = 0; i < member.constraints.size(); i++)
	{
		member.disconnect(member.constraints[i]);
	}
	member.lights.clear();
	member.cameras.clear();
	member.shapes.clear();
	member.nodes.clear();
	member.groups.clear();
	member.dirtySpace = true;
	member.refresh = true;
	if (member.sceneOctTree)
	{
		member.sceneOctTree->clear();
		member.nodeSpaceMap.clear();
		member.shapeSpaceMap.clear();
	}
	emit(Signal_Dirty);
	emit(Signal_Clear);
}

int BScene::lightCount() const
{
	return member.lights.size();
}
BLight* BScene::light(int index) const
{
	return member.lights(index);
}
BLight* BScene::light(const BString& name) const
{
	for (int i = 0; i < member.lights.size(); i++)
	{
		BLight* light = member.lights[i];
		if (light->name() == name)
			return light;
	}
	return 0;
}
BLight* BScene::lightOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.lights.size(); i++)
	{
		BLight* light = member.lights[i];
		if (light->asset() == asset)
			return light;
	}
	return 0;
}
bool BScene::lightsInSpace(const BSpace& space, BLightArray& lights) const
{
	lights.reset();
	if (member.sceneOctTree)
	{
		SceneOctTreeItem item = member.sceneOctTree->locate(space);
		member.collectLights(item, lights);
	}
	else
	{
		for (int i = 0; i < member.lights.size(); i++)
		{
			BLight* light = member.lights[i];
			if (light->space().intersect(space))
			{
				lights.append(light);
			}
		}
	}
	return lights.size();
}

int BScene::cameraCount() const
{
	return member.cameras.size();
}
BCamera* BScene::camera(int index) const
{
	return member.cameras(index);
}
BCamera* BScene::camera(const BString& name) const
{
	for (int i = 0; i < member.cameras.size(); i++)
	{
		BCamera* camera = member.cameras[i];
		if (camera->name() == name)
			return camera;
	}
	return 0;
}
BCamera* BScene::cameraOfAsset(const BValue& asset) const
{
	for (int i = 0; i < member.cameras.size(); i++)
	{
		BCamera* camera = member.cameras[i];
		if (camera->asset() == asset)
			return camera;
	}
	return 0;
}
bool BScene::camerasInSpace(const BSpace& space, BCameraArray& cameras) const
{
	cameras.reset();
	if (member.sceneOctTree)
	{
		SceneOctTreeItem item = member.sceneOctTree->locate(space);
		member.collectCameras(item, cameras);
	}
	else
	{
		for (int i = 0; i < member.cameras.size(); i++)
		{
			BCamera* camera = member.cameras[i];
			if (camera->space().intersect(space))
			{
				cameras.append(camera);
			}
		}
	}
	return cameras.size();
}

bool BScene::query(const BString& text, BValue& value) const
{
	if (text == "scene-tree")
	{
		value = member.sceneOctTree;
		return true;
	}
	return BObject::query(text, value);
}

void BScene::update(BReal time)
{

}
